﻿#region Declaration

//------------------------------------------------------------------------------
// Compas Information Techonoly Co., Ltd
// The Initial Developer of the Original Code is CompasSolutions.
// All Rights Reserved.
// 
// Contributor(s): _______. 
//------------------------------------------------------------------------------

#endregion

#region Using Directives

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.ComponentModel;

#endregion

namespace QuickDev.Common.Data.Sort
{
    /// <summary>
    /// Sorts a collection of objects by property names.
    /// </summary>
    /// <remarks>
    /// The sortExpression is similar to SQL order by clause. The format is:
    /// <code>
    /// Property1 [ASC|DESC][, Property2 [ASC|DESC], ...]
    /// </code>
    /// </remarks>
    public class CollectionSort<T>
    {
        /// <summary>
        /// Sorts an IList&lt;T&gt; by properties using the specified sort expression.
        /// </summary>
        public static void Sort( List<T> list , string sortExpression )
        {
            SortInfo[] sorts = BuildSorts( sortExpression );

            for ( int i = sorts.Length - 1 ; i >= 0 ; i-- )
            {
                SortInfo si = sorts[i];
                MergeSort<T>.Sort( list , si.Comparer , si.Direction );
            }
        }

        // parses the sort expression and builds the list of property comparers and directions
        private static SortInfo[] BuildSorts( string sortExpression )
        {
            Type itemType = typeof( T );
            string[] sortArray = sortExpression.Split( ',' );
            int arrayLength = sortArray.Length;
            List<SortInfo> comparers = new List<SortInfo>( arrayLength );

            // loops through each of the sort expressions, parse them and
            // add to the list
            for ( int i = 0 ; i < arrayLength ; i++ )
            {
                string sortExp = sortArray[i].Trim();

                ListSortDirection direction;
                string propertyName;
                int spacePos = sortExp.IndexOf( ' ' );

                // since there shouldn't be spaces on property names, spacePos >= 0
                // means that there should be an ASC or DESC in the expression
                if ( spacePos >= 0 )
                {
                    propertyName = sortExp.Substring( 0 , spacePos );
                    string sortOrder = sortExp.Substring( spacePos + 1 );

                    // checks the sort order
                    if ( String.Compare( sortOrder , "asc" , StringComparison.OrdinalIgnoreCase ) == 0 )
                        direction = ListSortDirection.Ascending;
                    else if ( String.Compare( sortOrder , "desc" , StringComparison.OrdinalIgnoreCase ) == 0 )
                        direction = ListSortDirection.Descending;
                    else
                        throw new ArgumentException( "Sort order '" + sortOrder + "' is invalid. Must be ASC or DESC." );
                }
                else
                {
                    propertyName = sortExp;
                    direction = ListSortDirection.Ascending;
                }

                PropertyComparer<T> comparer = new PropertyComparer<T>( propertyName );
                comparers.Add( new SortInfo( comparer , direction ) );
            }

            return comparers.ToArray();
        }

        // container class for the sort builder
        private class SortInfo
        {
            public SortInfo( PropertyComparer<T> comparer , ListSortDirection direction )
            {
                this.Comparer = comparer;
                this.Direction = direction;
            }

            public PropertyComparer<T> Comparer;
            public ListSortDirection Direction;
        }
    }
}